package com.duff.download.okdownload;

import android.content.ContentValues;
import android.content.Context;
import android.text.TextUtils;

import com.duff.download.okdownload.async.ParallelScheduler;
import com.duff.download.okdownload.async.SerialScheduler;
import com.duff.download.okdownload.async.Task;
import com.duff.download.okdownload.database.DownloadColumns;
import com.duff.download.okdownload.database.DownloadDBHelper;
import com.duff.download.okdownload.database.operator.Condition;
import com.duff.download.okdownload.database.operator.OperatorFactory;
import com.duff.download.okdownload.database.operator.QueryParameter;
import com.duff.download.okdownload.interfaces.DownloadTaskListener;
import com.duff.download.okdownload.model.DownloadInfo;
import com.duff.download.okdownload.utils.FileUtils;
import com.duff.download.okdownload.utils.Logger;
import com.duff.download.okdownload.utils.MD5Checksum;
import com.duff.download.okdownload.utils.StorageUtils;
import com.duff.download.okdownload.utils.Utils;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

/**
 * author：duff
 * version：1.0.0
 * date：2017/8/27
 * desc：下载管理器
 */
public class DownloadManager extends AbsDownloadManager<DownloadInfo> {

    public static final String DOWNLOAD_SUFFIX = "tmp"; // 下载临时文件后缀

    private Context mContext;
    private DownloadDBHelper mDownloadDBHelper;
    private DownloadEngine mDownloadEngine;
    private MD5Checksum mMD5Check = MD5Checksum.getDefault();

    private boolean mIsProcessed = false;

    private static DownloadManager sInstance;

    public static DownloadManager instance(Context context) {
        if (sInstance == null) {
            synchronized (DownloadManager.class) {
                if (sInstance == null) {
                    sInstance = new DownloadManager(context.getApplicationContext());
                }
            }
        }
        return sInstance;
    }

    private DownloadManager(Context context) {
        mContext = context;
        mDownloadDBHelper = new DownloadDBHelper(context, DownloadConfiguration.getDatabaseName());
        mDownloadEngine = new DownloadEngine();
    }

    public void setMaxRunningTask(int max) {
        mDownloadEngine.setMaxRunningTasks(max);
    }

    private DownloadTaskListener mDownloadTaskListener = new DownloadTaskListener() {

        @Override
        public void onStart(final long taskId, final long downloadBytes, final long totalBytes) {
            Logger.d("onStart");
            mWorkHandler.post(new Runnable() {
                @Override
                public void run() {
                    DownloadInfo info = mDownloadDBHelper.query(taskId);
                    if (info != null) {
                        info.setStatus(DownloadStatus.STATUS_STARTED);
                        info.setErrorCode(DownloadError.ERROR_SUCCESS);
                        info.setDownloadedBytes(downloadBytes);
                        info.setTotalSize(totalBytes);
                        info.setModifyTime(System.currentTimeMillis());
                        mDownloadDBHelper.update(info);

                        notifyUpdateStatus(info);
                    }
                }
            });
        }

        @Override
        public void onProgress(final long taskId, final long downloadBytes, final long totalBytes) {
            Logger.d("onProgress");
            ContentValues contentValues = new ContentValues();
            contentValues.put(DownloadColumns.COLUMN_TASK_ID, taskId);
            contentValues.put(DownloadColumns.COLUMN_TASK_DOWNLOADED, downloadBytes);
            contentValues.put(DownloadColumns.COLUMN_TASK_TOTAL, totalBytes);
            contentValues.put(DownloadColumns.COLUMN_TASK_MODIFY_TIME, System.currentTimeMillis());
            contentValues.put(DownloadColumns.COLUMN_TASK_ERROR, DownloadError.ERROR_SUCCESS);
            contentValues.put(DownloadColumns.COLUMN_TASK_STATUS, downloadBytes < totalBytes ? DownloadStatus.STATUS_STARTED : DownloadStatus.STATUS_DOWNLOAD_COMPLETED);
            mDownloadDBHelper.update(contentValues);

            mWorkHandler.post(new Runnable() {
                @Override
                public void run() {
                    DownloadInfo info = mDownloadDBHelper.query(taskId);
                    if (info != null) {
                        notifyProgress(info);
                    }
                }
            });

        }

        @Override
        public void onCanceled(final long taskId, final long downloadBytes, final long totalBytes) {
            Logger.d("onCanceled");
            mWorkHandler.post(new Runnable() {
                @Override
                public void run() {
                    DownloadInfo info = mDownloadDBHelper.query(taskId);
                    if (info != null && totalBytes > 0) {
                        int originStatus = info.getStatus();
                        if (downloadBytes == totalBytes) {
                            if (md5Check(info.getMD5(), info.getPath())) {
                                info.setStatus(DownloadStatus.STATUS_DOWNLOAD_COMPLETED);
                                info.setErrorCode(DownloadError.ERROR_SUCCESS);
                            } else {
                                info.setStatus(DownloadStatus.STATUS_ERROR);
                                info.setErrorCode(DownloadError.ERROR_MD5_NOT_MATCH);
                            }
                        } else {
                            info.setStatus(DownloadStatus.STATUS_PAUSED);
                        }

                        info.setDownloadedBytes(downloadBytes);
                        info.setTotalSize(totalBytes);
                        mDownloadDBHelper.update(info);

                        if (originStatus == DownloadStatus.STATUS_PAUSED
                                && info.getStatus() == DownloadStatus.STATUS_PAUSED) {
                            return;
                        }
                        if (originStatus == DownloadStatus.STATUS_ERROR
                                && info.getStatus() == DownloadStatus.STATUS_ERROR) {
                            return;
                        }
                        if (originStatus == DownloadStatus.STATUS_DOWNLOAD_COMPLETED
                                && info.getStatus() == DownloadStatus.STATUS_DOWNLOAD_COMPLETED) {
                            return;
                        }

                        notifyUpdateStatus(info);
                    }
                }
            });
        }

        @Override
        public void onError(final long taskId, int httpStatus, final int errorCode, String msg) {
            Logger.d("onError");
            mWorkHandler.post(new Runnable() {
                @Override
                public void run() {
                    DownloadInfo info = mDownloadDBHelper.query(taskId);
                    if (info != null) {
                        int originStatus = info.getStatus();

                        info.setStatus(DownloadStatus.STATUS_ERROR);
                        info.setErrorCode(errorCode);
                        mDownloadDBHelper.update(info);

                        if (originStatus == DownloadStatus.STATUS_ERROR
                                && info.getStatus() == DownloadStatus.STATUS_ERROR) {
                            return;
                        }

                        notifyUpdateStatus(info);
                    }
                }
            });
        }

        @Override
        public void onFinished(final long taskId, final long downloadBytes, final long totalBytes) {
            Logger.d("onFinished");
            mWorkHandler.post(new Runnable() {
                @Override
                public void run() {
                    DownloadInfo info = mDownloadDBHelper.query(taskId);
                    if (info != null) {
                        info.setDownloadedBytes(downloadBytes);
                        info.setTotalSize(totalBytes);

                        if (info.getStatus() == DownloadStatus.STATUS_STARTED
                                || info.getStatus() == DownloadStatus.STATUS_DOWNLOAD_COMPLETED) {
                            if (downloadBytes == totalBytes) {
                                // md5 check
                                if (md5Check(info.getMD5(), info.getPath())) {
                                    info.setStatus(DownloadStatus.STATUS_DOWNLOAD_COMPLETED);

                                    // After download complete, rename file suffix(xxx.apk.tmp -> xxx.apk)
                                    if (info.getPath().endsWith(DOWNLOAD_SUFFIX)) {// 排除将下载完成的物理文件后恢复下载的任务
                                        String destPath = info.getPath().substring(0, info.getPath().length() - DOWNLOAD_SUFFIX.length() - 1);
                                        File tmpFile = new File(info.getPath());
                                        if (tmpFile.renameTo(new File(destPath))) {
                                            info.setPath(destPath);
                                        }
                                    }
                                } else {
                                    info.setStatus(DownloadStatus.STATUS_ERROR);
                                    info.setErrorCode(DownloadError.ERROR_MD5_NOT_MATCH);
                                }
                            } else {
                                info.setStatus(DownloadStatus.STATUS_PAUSED);
                            }
                        }

                        mDownloadDBHelper.update(info);

                        notifyUpdateStatus(info);
                    }
                }
            });
        }
    };

    /**
     * 添加一个下载任务
     *
     * @param url
     * @param tag
     * @param path
     * @param taskName
     * @param callback
     */
    public void add(String url, String tag, String path, String taskName, AddCallback<DownloadInfo> callback) {
        DownloadInfo downloadInfo = new DownloadInfo();
        downloadInfo.setUrl(url);
        downloadInfo.setTag(tag);
        downloadInfo.setPath(path);
        downloadInfo.setTaskName(taskName);

        add(downloadInfo, callback);
    }

    /**
     * 添加一个下载任务
     *
     * @param downloadInfo
     * @param callback
     */
    @Override
    public void add(final DownloadInfo downloadInfo, final AddCallback<DownloadInfo> callback) {
        checkDownloadEnvironment(new OnCheckListener() {
            @Override
            public void onCheckFinished(boolean success) {
                if (success) {
                    SerialScheduler.execute(new Task<DownloadInfo, DownloadInfo>(downloadInfo) {
                        @Override
                        public DownloadInfo doInBackground(DownloadInfo downloadInfo) {
                            String path = genUniqueDownloadPath(downloadInfo.getPath(), DOWNLOAD_SUFFIX);
                            downloadInfo.setPath(path);
                            downloadInfo.setStatus(DownloadStatus.STATUS_PENDED);
                            DownloadInfo info = mDownloadDBHelper.add(downloadInfo);

                            return info;
                        }

                        @Override
                        public void onPostExecute(DownloadInfo downloadInfo) {
                            if (downloadInfo != null) {
                                mDownloadEngine.addTask(downloadInfo.getId(),
                                        downloadInfo.getUrl(),
                                        downloadInfo.getPath(),
                                        downloadInfo.getDownloadedBytes(),
                                        mDownloadTaskListener);
                            }
                            if (callback != null) {
                                callback.onAddFinished(DownloadError.ERROR_SUCCESS,
                                        downloadInfo == null ? null : new DownloadInfo[]{downloadInfo});
                            }
                        }
                    });
                }
            }
        }, callback, downloadInfo);
    }

    /**
     * 同时添加多个下载任务
     *
     * @param callback
     * @param downloadInfos
     */
    @Override
    public void add(final AddCallback<DownloadInfo> callback, final DownloadInfo... downloadInfos) {
        if (downloadInfos == null || downloadInfos.length == 0) {
            return;
        }
        checkDownloadEnvironment(new OnCheckListener() {
            @Override
            public void onCheckFinished(boolean success) {
                if (success) {
                    SerialScheduler.execute(new Task<DownloadInfo[], DownloadInfo[]>(downloadInfos) {
                        @Override
                        public DownloadInfo[] doInBackground(DownloadInfo[] downloadInfos) {
                            for (DownloadInfo info : downloadInfos) {
                                info.setPath(genUniqueDownloadPath(info.getPath(), DOWNLOAD_SUFFIX));
                                info.setStatus(DownloadStatus.STATUS_PENDED);
                            }
                            DownloadInfo[] infos = mDownloadDBHelper.add(downloadInfos);

                            return infos;
                        }

                        @Override
                        public void onPostExecute(DownloadInfo[] infos) {
                            if (infos != null) {
                                for (DownloadInfo info : infos) {
                                    mDownloadEngine.addTask(info.getId(),
                                            info.getUrl(),
                                            info.getPath(),
                                            info.getDownloadedBytes(),
                                            mDownloadTaskListener);
                                }
                            }
                            if (callback != null) {
                                callback.onAddFinished(DownloadError.ERROR_SUCCESS, infos);
                            }
                        }
                    });
                }
            }
        }, callback, downloadInfos);
    }

    @Override
    public void pause(final long downloadId) {
        ParallelScheduler.execute(new Task<Long, DownloadInfo>(downloadId) {
            @Override
            public DownloadInfo doInBackground(Long aLong) {
                DownloadInfo info = mDownloadDBHelper.query(downloadId);
                if (info != null) {
                    info.setStatus(DownloadStatus.STATUS_PAUSED);
                }
                mDownloadDBHelper.update(info);

                return info;
            }

            @Override
            public void onPostExecute(DownloadInfo downloadInfo) {
                if (downloadInfo != null) {
                    mDownloadEngine.cancelTask(downloadInfo.getId());

                    notifyUpdateStatus(downloadInfo);
                }
            }
        });
    }

    @Override
    public void pauseAll() {
        ParallelScheduler.execute(new Task<Object, DownloadInfo[]>(null) {
            @Override
            public DownloadInfo[] doInBackground(Object o) {
                QueryParameter queryParameter = new QueryParameter();
                String[] status = new String[]{
                        String.valueOf(DownloadStatus.STATUS_PENDED),
                        String.valueOf(DownloadStatus.STATUS_STARTED)
                };
                queryParameter.setCondition(new Condition().add(DownloadColumns.COLUMN_TASK_STATUS, OperatorFactory.in(status.length), status));
                DownloadInfo[] infos = mDownloadDBHelper.query(queryParameter);
                if (infos != null) {
                    for (DownloadInfo info : infos) {
                        info.setStatus(DownloadStatus.STATUS_PAUSED);
                    }
                    mDownloadDBHelper.update(infos);
                }

                return infos;
            }

            @Override
            public void onPostExecute(DownloadInfo[] downloadInfos) {
                if (downloadInfos != null) {
                    for (DownloadInfo info : downloadInfos) {
                        mDownloadEngine.cancelTask(info.getId());

                        notifyUpdateStatus(info);
                    }
                }
            }
        });
    }

    @Override
    public void resume(final long downloadId) {
        SerialScheduler.execute(new Task<Long, DownloadInfo>(downloadId) {
            @Override
            public DownloadInfo doInBackground(Long aLong) {
                DownloadInfo info = mDownloadDBHelper.query(downloadId);
                if (info != null) {
                    File targetFile = new File(info.getPath());
                    if (targetFile != null && targetFile.exists()) {
                        long downloaded = targetFile.length();
                        info.setDownloadedBytes(downloaded);
                    }
                    int status = isDownloadComplete(info) ? DownloadStatus.STATUS_DOWNLOAD_COMPLETED : DownloadStatus.STATUS_PENDED;
                    info.setStatus(status);
                    mDownloadDBHelper.update(info);
                }

                return info;
            }

            @Override
            public void onPostExecute(DownloadInfo downloadInfo) {
                if (downloadInfo != null) {
                    if (downloadInfo.getStatus() != DownloadStatus.STATUS_DOWNLOAD_COMPLETED) {
                        long rangeOffset = new File(downloadInfo.getPath()).exists() ? downloadInfo.getDownloadedBytes() : 0;
                        mDownloadEngine.addTask(downloadInfo.getId(), downloadInfo.getUrl(), downloadInfo.getPath(), rangeOffset, mDownloadTaskListener);
                    }

                    notifyUpdateStatus(downloadInfo);
                }
            }
        });
    }

    @Override
    public void resumeAll() {
        SerialScheduler.execute(new Task<Object, DownloadInfo[]>(null) {
            @Override
            public DownloadInfo[] doInBackground(Object o) {
                DownloadInfo[] infos = mDownloadDBHelper.queryUnComplete();
                if (infos != null) {
                    for (DownloadInfo info : infos) {
                        File targetFile = new File(info.getPath());
                        if (targetFile.exists()) {
                            long length = targetFile.length();
                            info.setDownloadedBytes(length);
                        }
                        int status = isDownloadComplete(info) ? DownloadStatus.STATUS_DOWNLOAD_COMPLETED : DownloadStatus.STATUS_PENDED;
                        info.setStatus(status);
                    }
                    mDownloadDBHelper.update(infos);
                }

                return infos;
            }

            @Override
            public void onPostExecute(DownloadInfo[] downloadInfos) {
                if (downloadInfos != null) {
                    for (DownloadInfo info : downloadInfos) {
                        if (info.getStatus() != DownloadStatus.STATUS_DOWNLOAD_COMPLETED) {
                            long rangeOffset = new File(info.getPath()).exists() ? info.getDownloadedBytes() : 0;
                            mDownloadEngine.addTask(info.getId(), info.getUrl(), info.getPath(), rangeOffset, mDownloadTaskListener);
                        }

                        notifyUpdateStatus(info);
                    }
                }
            }
        });
    }

    /**
     * 下载完成?
     *
     * @param downloadInfo
     * @return
     */
    private boolean isDownloadComplete(DownloadInfo downloadInfo) {
        if (downloadInfo == null) {
            return false;
        }
        return (downloadInfo.getTotalSize() > 0 && downloadInfo.getDownloadedBytes() == downloadInfo.getTotalSize());
    }

    @Override
    public void delete(final boolean onlyDB, final long downloadId, final DeleteCallback<DownloadInfo> callback) {
        SerialScheduler.execute(new Task<Long, DownloadInfo>(downloadId) {
            @Override
            public DownloadInfo doInBackground(Long aLong) {
                DownloadInfo info = mDownloadDBHelper.query(downloadId);
                if (info != null) {
                    mDownloadDBHelper.delete(downloadId);
                    if (!onlyDB) {
                        new File(info.getPath()).delete();
                    }
                }
                return info;
            }

            @Override
            public void onPostExecute(DownloadInfo downloadInfo) {
                mDownloadEngine.cancelTask(downloadId);
                if (callback != null) {
                    callback.onDeleteFinished(DownloadError.ERROR_SUCCESS, new DownloadInfo[]{downloadInfo});
                }
            }
        });
    }

    @Override
    public void delete(final boolean onlyDB, final DeleteCallback<DownloadInfo> callback, final long... downloadIds) {
        if (downloadIds == null || downloadIds.length == 0) {
            return;
        }
        SerialScheduler.execute(new Task<Object, DownloadInfo[]>(null) {
            @Override
            public DownloadInfo[] doInBackground(Object o) {
                int size = downloadIds.length;

                String[] ids = new String[size];
                for (int i = 0; i < size; i++) {
                    ids[i] = String.valueOf(downloadIds[i]);
                }
                QueryParameter queryParameter = new QueryParameter();
                queryParameter.setCondition(new Condition().add(DownloadColumns.COLUMN_TASK_ID, OperatorFactory.in(size), ids));
                DownloadInfo[] infos = mDownloadDBHelper.query(queryParameter);
                mDownloadDBHelper.delete(downloadIds);

                if (infos != null && !onlyDB) {
                    for (DownloadInfo info : infos) {
                        new File(info.getPath()).delete();
                    }
                }

                return infos;
            }

            @Override
            public void onPostExecute(DownloadInfo[] downloadInfos) {
                for (long id : downloadIds) {
                    mDownloadEngine.cancelTask(id);
                }
                if (callback != null) {
                    callback.onDeleteFinished(DownloadError.ERROR_SUCCESS, downloadInfos);
                }
            }
        });
    }

    @Override
    public void deleteAll(final DeleteCallback<DownloadInfo> callback) {
        SerialScheduler.execute(new Task<Object, DownloadInfo[]>(null) {
            @Override
            public DownloadInfo[] doInBackground(Object o) {
                DownloadInfo[] infos = mDownloadDBHelper.queryAll();
                mDownloadDBHelper.deleteAll();

                if (infos != null) {
                    for (DownloadInfo info : infos) {
                        new File(info.getPath()).delete();
                    }
                }

                return infos;
            }

            @Override
            public void onPostExecute(DownloadInfo[] downloadInfos) {
                for (DownloadInfo info : downloadInfos) {
                    mDownloadEngine.cancelTask(info.getId());
                }
                if (callback != null) {
                    callback.onDeleteFinished(DownloadError.ERROR_SUCCESS, downloadInfos);
                }
            }
        });
    }

    @Override
    public void queryAll(final QueryCallback<DownloadInfo> callback) {
        ParallelScheduler.execute(new Task<Object, DownloadInfo[]>(null) {
            @Override
            public DownloadInfo[] doInBackground(Object o) {
                processData();

                return mDownloadDBHelper.queryAll();
            }

            @Override
            public void onPostExecute(DownloadInfo[] downloadInfos) {
                if (callback != null) {
                    callback.onQueryFinished(DownloadError.ERROR_SUCCESS, downloadInfos);
                }
            }
        });
    }

    @Override
    public void query(final QueryParameter parameter, final QueryCallback<DownloadInfo> callback) {
        ParallelScheduler.execute(new Task<QueryParameter, DownloadInfo[]>(parameter) {
            @Override
            public DownloadInfo[] doInBackground(QueryParameter queryParameter) {
                processData();

                return mDownloadDBHelper.query(queryParameter);
            }

            @Override
            public void onPostExecute(DownloadInfo[] downloadInfos) {
                if (callback != null) {
                    callback.onQueryFinished(DownloadError.ERROR_SUCCESS, downloadInfos);
                }
            }
        });
    }

    /**
     * 处理异常退出的数据,首次查询的时候需要处理异常数据
     */
    private void processData() {
        if (!mIsProcessed) {
            // 将开始和等待状态的任务置为暂停状态
            QueryParameter queryParameter = new QueryParameter();
            queryParameter.setCondition(new Condition()
                    .add(DownloadColumns.COLUMN_TASK_STATUS, OperatorFactory.equal(), DownloadStatus.STATUS_STARTED)
                    .or()
                    .add(DownloadColumns.COLUMN_TASK_STATUS, OperatorFactory.equal(), DownloadStatus.STATUS_PENDED)
            );
            ContentValues contentValues = new ContentValues();
            contentValues.put(DownloadColumns.COLUMN_TASK_STATUS, DownloadStatus.STATUS_PAUSED);
            mDownloadDBHelper.update(contentValues, queryParameter);

            // 根据物理文件重置任务状态
            DownloadInfo[] downloadInfos = mDownloadDBHelper.queryAll();
            if (downloadInfos != null && downloadInfos.length > 0) {
                for (DownloadInfo info : downloadInfos) {
                    File file = new File(info.getPath());
                    if (file.exists()) {
                        info.setDownloadedBytes(file.length());
                    } else {
                        info.setDownloadedBytes(0);
                        info.setStatus(DownloadStatus.STATUS_PAUSED);
                    }
                }
                mDownloadDBHelper.update(downloadInfos);
            }
        }
        mIsProcessed = true;
    }

    /**
     * 检查下载环境
     *
     * @param listener
     * @param callback
     * @param downloadInfos
     */
    private void checkDownloadEnvironment(final OnCheckListener listener, final AddCallback<DownloadInfo> callback, DownloadInfo... downloadInfos) {
        // 检查网络有效性
        if (!Utils.isNetWorkAvailable(mContext)) {
            if (callback != null) {
                if (callback.onAddFinished(DownloadError.ERROR_NETWORK, downloadInfos)) {
                    return;
                }
            }
        }

        // 检查SDCard有效性
        if (!StorageUtils.isSdCardWritable()) {
            if (callback != null) {
                if (callback.onAddFinished(DownloadError.ERROR_SDCARD_INVALID, downloadInfos)) {
                    return;
                }
            }
        }

        // 检查SDCard容量
        File file = new File(downloadInfos[0].getPath());
        if (StorageUtils.hasNoMoreFreeSpace(file.getParent())) {
            if (callback != null) {
                if (callback.onAddFinished(DownloadError.ERROR_SDCARD_FULL, downloadInfos)) {
                    return;
                }
            }
        }

        // 检查url有效性
        List<DownloadInfo> invalidUrlItems = new ArrayList<>();
        for (DownloadInfo info : downloadInfos) {
            if (!Utils.isHttpUrl(info.getUrl())) {
                invalidUrlItems.add(info);
            }
            if (invalidUrlItems != null && !invalidUrlItems.isEmpty()) {
                if (callback != null) {
                    DownloadInfo[] tmpInfos = new DownloadInfo[invalidUrlItems.size()];
                    invalidUrlItems.toArray(tmpInfos);
                    if (callback.onAddFinished(DownloadError.ERROR_INVALID_URL, tmpInfos)) {
                        return;
                    }
                }
            }
        }

        // 检查重复下载，如果数据库中不存在记录，但是物理文件存在，则将物理文件删除
        SerialScheduler.execute(new Task<DownloadInfo[], DownloadInfo[]>(downloadInfos) {
            @Override
            public DownloadInfo[] doInBackground(DownloadInfo[] downloadInfos) {
                List<DownloadInfo> downloadInfosInDB = new ArrayList<>();
                for (DownloadInfo info : downloadInfos) {
                    QueryParameter queryParameter = new QueryParameter();
                    queryParameter.setCondition(new Condition().add(DownloadColumns.COLUMN_TASK_URL, OperatorFactory.equal(), info.getUrl()));
                    DownloadInfo[] infos = mDownloadDBHelper.query(queryParameter);
                    if (infos != null && infos.length > 0) {
                        downloadInfosInDB.add(info);
                    } else {
                        File downloadFile = new File(info.getPath());
                        if (downloadFile.exists()) {
                            downloadFile.delete();
                        }

                        String tmpPath = info.getPath() + "." + DOWNLOAD_SUFFIX;
                        File tmpFile = new File(tmpPath);
                        if (tmpFile.exists()) {
                            tmpFile.delete();
                        }
                    }
                }
                DownloadInfo[] tmpInfos = new DownloadInfo[downloadInfosInDB.size()];
                downloadInfosInDB.toArray(tmpInfos);

                return tmpInfos;
            }

            @Override
            public void onPostExecute(DownloadInfo[] downloadInfos) {
                if (downloadInfos != null && downloadInfos.length > 0) {
                    if (callback != null) {
                        if (!callback.onAddFinished(DownloadError.ERROR_DUPLICATE_DOWNLOAD, downloadInfos) && listener != null) {
                            listener.onCheckFinished(true);
                        }
                    }
                } else if (listener != null) {
                    listener.onCheckFinished(true);
                }
            }
        });
    }

    /**
     * 生成唯一的文件名
     *
     * @param path
     * @param fixedSuffix
     * @return
     */
    private static String genUniqueDownloadPath(String path, String fixedSuffix) {
        int number = 0;
        String directory = FileUtils.getFilePathDir(path);
        String shortFileName = FileUtils.getFileShortName(path); // 文件名，不包含后缀
        String suffix = FileUtils.getFileExtension(path); // 后缀
        String uniqueFilename = path;

        String unionSuffix = TextUtils.isEmpty(suffix) ? fixedSuffix : suffix + "." + fixedSuffix;
        String unionSuffixFilename = directory + File.separator + shortFileName + "." + unionSuffix;

        File file = new File(uniqueFilename);
        File unionFile = new File(unionSuffixFilename);
        while (file.exists() || unionFile.exists()) {
            ++number;
            uniqueFilename = directory + File.separator + shortFileName + "_" + number + "." + suffix;
            unionSuffixFilename = directory + File.separator + shortFileName + "_" + number + "." + unionSuffix;
            file = new File(uniqueFilename);
            unionFile = new File(unionSuffixFilename);
        }
        return unionSuffixFilename;
    }

    /**
     * md5检查
     *
     * @param md5
     * @param path
     * @return md5值不存在，return true， 否则进行md5检查
     */
    private boolean md5Check(String md5, String path) {
        if (TextUtils.isEmpty(md5)) {
            return true;
        }
        return mMD5Check.check(md5, new File(path));
    }

}
